package EA.operators.mutation;
import EA.*;
import RKUjava.util.*;
import RKUjava.math.*;

/** Normal Distributed mutation with exponentially distributed variance for numerical genomes.
 */
public class NumExpMutation extends NumMutation
{

  /** Mutate all entries in a given genome. This
      mutation operator will only mutate a specific number of the entries in
      the vector represented by the numerical genome. If the genome
      contains 200 parameters and you ask this method to mutate 10% of
      the genome then 20 of the entries will be changed. The entries
      are picked at random and no checking is done to ensure that an
      entry is only mutated once (time consuming).
      @param genome The NumericalGenome to mutate.
      @param params Parameters for the mutation operator. 
      <ul>
      <li>params[0] = probability of mutation.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      </ul>
  */
  public final static void mutateAll(NumericalGenome genome, double[] params)
    {
      int number;
      double adder;
      int i;
      double[] pos;
      Interval curinterval;

      if (UsefulRoutines.randomBiasedBoolean(params[0])) {
	pos = genome.getPosReference();
	number = pos.length;
      
	for (i=0;i<number;i++) {
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*params[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params[1]));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	}
	genome.setPosReference(pos);
	genome.fitnesscalculated = false;
      }
    }

  /** Mutate all entries in a given genome. This
      mutation operator will only mutate a specific number of the entries in
      the vector represented by the numerical genome. If the genome
      contains 200 parameters and you ask this method to mutate 10% of
      the genome then 20 of the entries will be changed. The entries
      are picked at random and no checking is done to ensure that an
      entry is only mutated once (time consuming).
      @param genome The NumericalGenome to mutate.
      @param params1 Parameters for the mutation operator. 
      <ul>
      <li>params[0] = probability of mutation.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      </ul>
      @param params2 Mean values for normal distribution for each entry (will be scaled by interval length).
  */
  public static void mutateAll(NumericalGenome genome, double[] params1, double[] params2)
    {
      int number;
      double adder;
      int i;
      double[] pos;
      Interval curinterval;

      if (UsefulRoutines.randomBiasedBoolean(params1[0])) {
	pos = genome.getPosReference();
	number = pos.length;
      
	for (i=0;i<number;i++) {
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*(params2[i]+params1[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params1[1])));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	}
	genome.setPosReference(pos);
	genome.fitnesscalculated = false;
      }

    }


  /** Mutate a fraction of a given genome. This mutation operator will
      only mutate a fraction of the entries in the vector represented
      by the numerical genome. If the genome contains 200 parameters and 
      you ask this method to mutate 10% of the genome then 20 of the entries
      will be changed. The entries are picked at random and no
      checking is done to ensure that an entry is only mutated once
      (time consuming).
      @param genome The NumericalGenome to mutate.
      @param params Parameters for the mutation operator.
      <ul>
      <li>params[0] = probability of mutation.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      <li>params[3] = fraction of entries to mutate (value between 0 and 1).
      </ul>
  */
  public final static void mutateFraction(NumericalGenome genome, double[] params)
    {
      int frac;
      double adder;
      int i,cnt;
      double[] pos;
      Interval curinterval;

      if (UsefulRoutines.randomBiasedBoolean(params[0])) {
	pos = genome.getPosReference();
	frac = Math.max(1,(int)(params[3]*pos.length));
      
	for (cnt=0;cnt<frac;cnt++) {
	  i = UsefulRoutines.randomInt(pos.length);
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*params[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params[1]));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	  
	}
	genome.setPosReference(pos);
	genome.fitnesscalculated = false;
      }
    }

  /** Mutate a fraction of a given genome. This mutation operator will
      only mutate a fraction of the entries in the vector represented
      by the numerical genome. If the genome contains 200 parameters and 
      you ask this method to mutate 10% of the genome then 20 of the entries
      will be changed. The entries are picked at random and no
      checking is done to ensure that an entry is only mutated once
      (time consuming).
      @param genome The NumericalGenome to mutate.
      @param params1 Parameters for the mutation operator.
      <ul>
      <li>params[0] = probability of mutation.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      <li>params[3] = fraction of entries to mutate (value between 0 and 1).
      </ul>
      @param params2 Mean values for normal distribution for each entry (will be scaled by interval length).
  */
  public final static void mutateFraction(NumericalGenome genome, double[] params1, double[] params2)
    {
      int frac;
      double adder;
      int i,cnt;
      double[] pos;
      Interval curinterval;

      if (UsefulRoutines.randomBiasedBoolean(params1[0])) {
	pos = genome.getPosReference();
	frac = Math.max(1,(int)(params1[3]*pos.length));
      
	for (cnt=0;cnt<frac;cnt++) {
	  i = UsefulRoutines.randomInt(pos.length);
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*(params2[i]+params1[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params1[1])));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	  
	}
	genome.setPosReference(pos);
	genome.fitnesscalculated = false;
      }
    }

  /** Mutate a fixed number of entries in a given genome. This
      mutation operator will only mutate a specific number of the
      entries in the vector represented by the numerical genome. If
      the genome contains 200 parameters and you ask this method to
      mutate 10% of the genome then 20 of the entries will be
      changed. The entries are picked at random and no checking is
      done to ensure that an entry is only mutated once (time
      consuming).
      @param genome The NumericalGenome to mutate.
      @param params Parameters for the mutation operator.
      <ul>
      <li>params[0] = probability of mutation.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      <li>params[3] = number of entries to mutate.
      </ul>
  */
  public final static void mutateFixedNumber(NumericalGenome genome, double[] params)
    {
      int number;
      double adder;
      int i, cnt;
      double[] pos;
      Interval curinterval;

      if (UsefulRoutines.randomBiasedBoolean(params[0])) {
	pos = genome.getPosReference();
	number = Math.max(1,(int)params[3]);
      
	for (cnt=0;cnt<number;cnt++) {
	  i = UsefulRoutines.randomInt(pos.length);
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*params[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params[1]));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	}
	genome.setPosReference(pos);
	genome.fitnesscalculated = false;
      }
    }

  /** Mutate a fixed number of entries in a given genome. This
      mutation operator will only mutate a specific number of the
      entries in the vector represented by the numerical genome. If
      the genome contains 200 parameters and you ask this method to
      mutate 10% of the genome then 20 of the entries will be
      changed. The entries are picked at random and no checking is
      done to ensure that an entry is only mutated once (time
      consuming).
      @param genome The NumericalGenome to mutate.
      @param params1 Parameters for the mutation operator.
      <ul>
      <li>params[0] = probability of mutation.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      <li>params[3] = number of entries to mutate.
      </ul>
      @param params2 Mean values for normal distribution for each entry (will be scaled by interval length).
  */
  public final static void mutateFixedNumber(NumericalGenome genome, double[] params1, double[] params2)
    {
      int number;
      double adder;
      int i, cnt;
      double[] pos;
      Interval curinterval;

      if (UsefulRoutines.randomBiasedBoolean(params1[0])) {
	pos = genome.getPosReference();
	number = Math.max(1,(int)params1[3]);
      
	for (cnt=0;cnt<number;cnt++) {
	  i = UsefulRoutines.randomInt(pos.length);
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*(params2[i]+params1[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params1[1])));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	}
	genome.setPosReference(pos);
	genome.fitnesscalculated = false;
      }
    }

  /** Mutate entries in the given vector such that each entry has a
      low probability of being mutated. This mutation operator will
      traverse the entries in the genome, and with the given
      probability mutate the entry.
      @param genome The NumericalGenome to mutate.
      @param params Parameters for the mutation operator. 
      <ul>
      <li>params[0] = probability of mutation an entry.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      </ul>
  */
  public final static void mutateRollEach(NumericalGenome genome, double[] params)
    {
      double adder;
      int i;
      double[] pos;
      Interval curinterval;

      pos = genome.getPosReference();
      
      for (i=0;i<pos.length;i++) {
	if (UsefulRoutines.randomBiasedBoolean(params[0])) {
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*params[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params[1]));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	}
      }
      genome.setPosReference(pos);
      genome.fitnesscalculated = false;
    }

  /** Mutate entries in the given vector such that each entry has a
      low probability of being mutated. This mutation operator will
      traverse the entries in the genome, and with the given
      probability mutate the entry.
      @param genome The NumericalGenome to mutate.
      @param params1 Parameters for the mutation operator. 
      <ul>
      <li>params[0] = probability of mutation an entry.
      <li>params[1] = lambda parameter for the exponential distribution.
      <li>params[2] = scaling constant for interval (usually 1/10). 
      </ul>
      @param params2 Mean values for normal distribution for each entry (will be scaled by interval length).
  */
  public final static void mutateRollEach(NumericalGenome genome, double[] params1, double[] params2)
    {
      double adder;
      int i;
      double[] pos;
      Interval curinterval;

      pos = genome.getPosReference();
      
      for (i=0;i<pos.length;i++) {
	if (UsefulRoutines.randomBiasedBoolean(params1[0])) {
	  curinterval = ((NumericalFramework)genome.framework).intervals[i];
	  adder = curinterval.getLength()*(params2[i]+params1[2]*RKURandom.getNormalDistributed(0,RKURandom.getExponentialDistributed(params1[1])));
	  pos[i] = curinterval.bringInClosedRange(pos[i]+adder);
	}
      }
      genome.setPosReference(pos);
      genome.fitnesscalculated = false;
    }

}
